#include "qpropertylistview.h"

#include "qpropertybaseeditor.h"

#include "../sharedlib/qabstractproperty.h"
#include "script_edit/qscripteditdialog.h"
#include "../sharedlib/qbtneditorlabel.h"
#include "stylesheeteditor/qstylesheeteditor.h"
#include "qundocommand/qpropertyundocommand.h"
#include "qundocommand/qcommandobject.h"

#include "../sharedLib/qabstracthost.h"
#include "../coreLib/qsoftcore.h"
#include "../sharedLib/qcommonstruct.h"
#include "qstringeditdialog.h"


#include <QItemDelegate>
#include <QPainter>
#include <QApplication>
#include <QHeaderView>
#include <QMetaObject>
#include <QMetaProperty>
#include <QPalette>
#include <QMouseEvent>

using namespace CORELIB;

static QIcon drawIndicatorIcon(const QPalette &palette,QStyle *style)
{
    QPixmap pix(14,14);

    pix.fill(Qt::transparent);

    QStyleOption branchOption;
    QRect r(QPoint(0,0),pix.size());
    branchOption.rect=QRect(2,2,9,9);
    branchOption.palette=palette;
    branchOption.state=QStyle::State_Children;

    QPainter p;
    p.begin(&pix);
    style->drawPrimitive(QStyle::PE_IndicatorBranch,&branchOption,&p);
    p.end();
    QIcon rc=pix;
    rc.addPixmap(pix,QIcon::Selected,QIcon::Off);
    branchOption.state |=QStyle::State_Open;

    pix.fill(Qt::transparent);
    p.begin(&pix);
    style->drawPrimitive(QStyle::PE_IndicatorBranch,&branchOption,&p);
    p.end();

    rc.addPixmap(pix,QIcon::Normal,QIcon::On);
    rc.addPixmap(pix,QIcon::Selected,QIcon::On);
    return rc;
}

QWidget *QPropertyEditorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/*option*/, const QModelIndex &index) const
{
    QWidget *ret=NULL;
    if(index.column()==1)
    {
        QTreeWidgetItem *item=m_listView->indexToItem(index);
        SHAREDLIB::QAbstractProperty *property=m_listView->itemToProperty(item);
        if(property!=NULL)
        {
            ret=property->getEditor();
            m_editedItem.insert(0,item);
            if(ret!=NULL)
            {
                ret->metaObject()->userProperty().write(ret,property->getValue());
                m_editor=ret;
                connect(property,SIGNAL(showValueChanged()),this,SLOT(propertyValueChanged()));
                connect(property,SIGNAL(destroyed()),this,SLOT(propertyDestroyed()));
                QPropertyBaseEditor *wid=new QPropertyBaseEditor(property,parent);
                wid->setEditorWidget(ret);
                wid->enabledButton(property->attribute("modified").toBool());
                wid->setReset(property->attribute("reset").toBool());
                if(ret->metaObject()->className()==QString("SHAREDLIB::QBtnEditorLabel"))
                {
                    SHAREDLIB::QBtnEditorLabel *l=(SHAREDLIB::QBtnEditorLabel*)ret;
                    connect(l,SIGNAL(btnClicked()),m_listView,SLOT(edit()));
                }
                ret=wid;
                connect(ret,SIGNAL(destroyed(QObject*)),this,SLOT(slotEditorDestroyed(QObject*)));
                m_property=property;
            }
        }
    }
    return ret;
}

void QPropertyEditorDelegate::propertyValueChanged()
{
    if(m_editor==NULL)
    {
        return;
    }
    SHAREDLIB::QAbstractProperty* pro=(SHAREDLIB::QAbstractProperty*)sender();
    m_editor->metaObject()->userProperty().write(m_editor,pro->getValue());
}

void QPropertyEditorDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/*index*/) const
{
    editor->setGeometry(option.rect.adjusted(0,0,0,-1));
}

QTreeWidgetItem *QPropertyEditorDelegate::editerItem()
{
    return m_editedItem.size()>0?m_editedItem.first():NULL;
}

void QPropertyEditorDelegate::slotEditorDestroyed(QObject *object)
{
    if(qobject_cast<QWidget*>(object)!=NULL)
    {
        m_editedItem.removeLast();
        if(m_property!=NULL)
        {
            disconnect(m_property,SIGNAL(showValueChanged()),this,SLOT(propertyValueChanged()));
        }
        m_editor=NULL;
        m_property=NULL;
    }
}

void QPropertyEditorDelegate::propertyDestroyed()
{
    if(m_property==(SHAREDLIB::QAbstractProperty*)sender())
    {
        m_property=NULL;
    }
}

QSize QPropertyEditorDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QSize size=QItemDelegate::sizeHint(option,index);
    size.setHeight(21);
    size.rwidth()+=4;
    return size;
}

void QPropertyEditorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QTreeWidgetItem* item=m_listView->indexToItem(index);
    QStyleOptionViewItemV3 opt=option;
    QColor c;
    if(item!=NULL)
    {
        SHAREDLIB::QAbstractProperty *p=m_listView->itemToProperty(item);

        if(p!=NULL)
        {
            if(p->attribute("modified").toBool() && index.column()==0 && p->parent()==NULL)
            {
                opt.font.setBold(true);
                opt.fontMetrics=QFontMetrics(opt.font);
            }
            QModelIndex temp=index;
            while(temp.isValid() && !m_listView->isGroupItem(m_listView->indexToItem(temp)))
            {
                temp=temp.parent();
            }
            if(temp.isValid())
            {
                QTreeWidgetItem *tempItem=m_listView->indexToItem(temp);
                c=m_listView->m_colors.at(m_listView->groupIndex(tempItem)%m_listView->m_colors.count()).first;
            }
            else
            {
                c=m_listView->m_colors.at(0).first;
            }

            if(opt.features & QStyleOptionViewItemV2::Alternate)
            {
                c=c.lighter(112);
            }

        }
        else
        {
            c=opt.palette.color(QPalette::Dark);
            opt.palette.setColor(QPalette::Text,opt.palette.color(QPalette::BrightText));
        }
    }



    painter->fillRect(option.rect,c);
    opt.state &=~ QStyle::State_HasFocus;
    QItemDelegate::paint(painter,opt,index);
    opt.palette.setCurrentColorGroup(QPalette::Active);
    QColor color=static_cast<QRgb>(qApp->style()->styleHint(QStyle::SH_Table_GridLineColor,&opt));
    painter->setPen(color);

    if(!m_listView->isGroupItem(item))
    {
        if(index.column()==0)
        {
            int right=option.direction==Qt::LeftToRight?option.rect.right():option.rect.left();
            painter->drawLine(right,option.rect.y(),right,option.rect.bottom());
        }
    }
}


QPropertyListView::QPropertyListView(QWidget *parent):
    QTreeWidget(parent)
{
    setFrameStyle(QFrame::NoFrame);
    setIconSize(QSize(18, 18));
    setColumnCount(2);
    QStringList labels;
    labels.append(tr("Property"));
    labels.append(tr("Value"));
    setHeaderLabels(labels);
    setAlternatingRowColors(true);
    setEditTriggers(QAbstractItemView::EditKeyPressed);
    setRootIsDecorated(false);
    header()->setMovable(false);

    QVector<QColor> colors;
    colors.reserve(6);
    colors.push_back(QColor(255,230,191));
    colors.push_back(QColor(255,255,191));
    colors.push_back(QColor(191,255,191));
    colors.push_back(QColor(199,255,255));
    colors.push_back(QColor(234,191,255));
    colors.push_back(QColor(255,191,239));
    m_colors.reserve(colors.count());
    const int darknessFactor=250;
    for(int i=0;i<colors.count();i++)
    {
        QColor c=colors.at(i);
        m_colors.push_back(qMakePair(c,c.darker(darknessFactor)));
    }

    m_delegate=new QPropertyEditorDelegate(this);
    setItemDelegate(m_delegate);

    m_expandIcon=drawIndicatorIcon(this->palette(),this->style());

    QPalette p=this->palette();
    p.setColor(QPalette::Inactive,QPalette::Highlight,p.color(QPalette::Active,QPalette::Highlight));
    p.setColor(QPalette::Inactive,QPalette::HighlightedText,p.color(QPalette::Active,QPalette::HighlightedText));
    this->setPalette(p);
}

void QPropertyListView::setPropertys(const QList<SHAREDLIB::QAbstractProperty *> &propertys)
{
    clearAll();
    m_propertys=propertys;
    updateView();
    this->expandAll();
}

void QPropertyListView::clearAll()
{
    this->clear();
    m_groupItems.clear();
    m_propertyToItem.clear();
    m_itemToProperty.clear();
    m_groups.clear();
}

void QPropertyListView::updateView()
{
    foreach(SHAREDLIB::QAbstractProperty* pro,m_propertys)
    {
        insertItem(pro);
    }
}

void QPropertyListView::insertItem(SHAREDLIB::QAbstractProperty *property, SHAREDLIB::QAbstractProperty *parent)
{
    if(property==NULL)
    {
        return;
    }
    if(!property->attribute("visible").toBool())
    {
        return;
    }
    QTreeWidgetItem *parentItem;
    if(parent==NULL)
    {
        QString str=property->attribute("group").toString();
        if(str=="")
        {
            str=tr("Common");
        }
        parentItem=m_groupItems.value(str);
        if(parentItem==NULL)
        {
            parentItem=new QTreeWidgetItem(this);
            parentItem->setText(0,str);
            parentItem->setIcon(0,m_expandIcon);
            parentItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
            m_groupItems.insert(str,parentItem);
            m_groups.append(parentItem);
        }
    }
    else
    {
        parentItem=m_propertyToItem.value(parent);
    }
    if(parentItem==NULL)
    {
        return;
    }
    QTreeWidgetItem *item=new QTreeWidgetItem(parentItem);
    item->setText(0,property->attribute("property_name").toString());
    item->setToolTip(0,property->attribute("property_name").toString());
    item->setText(1,property->valueText());
    item->setToolTip(1,property->valueText());
    item->setIcon(1,property->valueIcon());
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable);
    m_itemToProperty.insert(item,property);
    m_propertyToItem.insert(property,item);
    connect(property,SIGNAL(showValueChanged()),this,SLOT(propertyShowValueChanged()));
    connect(property,SIGNAL(attributeChanged(QString)),this,SLOT(propertyAttributeChanged(QString)));
    connect(property,SIGNAL(value_changed(QVariant,QVariant)),this,SLOT(property_value_changed(QVariant,QVariant)));
    QList<SHAREDLIB::QAbstractProperty*>    pros=property->childrenProperty();
    foreach(SHAREDLIB::QAbstractProperty* pro,pros)
    {
        insertItem(pro,property);
    }
}

QTreeWidgetItem *QPropertyListView::indexToItem(const QModelIndex &index) const
{
    return itemFromIndex(index);
}

SHAREDLIB::QAbstractProperty  * QPropertyListView::itemToProperty(QTreeWidgetItem *item) const
{
    return m_itemToProperty.value(item,NULL);
}

bool QPropertyListView::isGroupItem(QTreeWidgetItem *item)const
{
    return m_groupItems.values().contains(item);
}

int QPropertyListView::groupIndex(QTreeWidgetItem *item) const
{
    return m_groups.indexOf(item);
}

void QPropertyListView::mousePressEvent(QMouseEvent *event)
{
    QTreeWidget::mousePressEvent(event);

    QTreeWidgetItem *item=itemAt(event->pos());

    if(item)
    {
        if(item!=m_delegate->editerItem() && (event->button()==Qt::LeftButton)
                && (header()->logicalIndexAt(event->pos())==1)
                && ((item->flags() &(Qt::ItemIsEditable | Qt::ItemIsEnabled))==(Qt::ItemIsEditable | Qt::ItemIsEnabled)))
        {
            editItem(item,1);
        }
        else if(m_groupItems.values().contains(item))
        {
            if(event->pos().x()+header()->offset()<20)
            {
                item->setExpanded(!item->isExpanded());
            }
        }
    }
}

void QPropertyListView::drawRow(QPainter *painter, const QStyleOptionViewItem &options, const QModelIndex &index) const
{
    QModelIndex temp=index;
    while(temp.isValid() && !isGroupItem(indexToItem(temp)))
    {
        temp=temp.parent();
    }
    QTreeWidgetItem* item=indexToItem(index);
    QTreeWidgetItem* tempItem=indexToItem(temp);
    QStyleOptionViewItemV3 opt=options;
    QColor c;
    if(item!=NULL)
    {
        SHAREDLIB::QAbstractProperty  *p=this->itemToProperty(item);
        if(p!=NULL)
        {

            c=m_colors.at(groupIndex(tempItem)%m_colors.count()).first;
            painter->fillRect(options.rect,c);
            opt.palette.setColor(QPalette::AlternateBase,c.lighter(112));
        }
        else
        {
            c=opt.palette.color(QPalette::Dark);
            painter->fillRect(options.rect,c);
            opt.palette.setColor(QPalette::AlternateBase,c);
        }
    }


    QTreeWidget::drawRow(painter,opt,index);

    QColor color=static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor));
    painter->setPen(color);
    painter->drawLine(opt.rect.x(),opt.rect.bottom(),opt.rect.right(),opt.rect.bottom());

}

void QPropertyListView::propertyShowValueChanged()
{
    SHAREDLIB::QAbstractProperty    *pro=(SHAREDLIB::QAbstractProperty*)sender();
    QTreeWidgetItem* item=m_propertyToItem.value(pro);
    if(item==NULL)
    {
        return;
    }
    item->setText(1,pro->valueText());
    item->setIcon(1,pro->valueIcon());
}

void QPropertyListView::propertyAttributeChanged(const QString &key)
{
    SHAREDLIB::QAbstractProperty    *pro=(SHAREDLIB::QAbstractProperty*)sender();
    QTreeWidgetItem* item=m_propertyToItem.value(pro);
    if(key=="modified")
    {
        this->update(this->indexFromItem(item,0));
    }
}

void QPropertyListView::edit()
{
    SHAREDLIB::QBtnEditorLabel *l=(SHAREDLIB::QBtnEditorLabel*)sender();
    SHAREDLIB::QAbstractProperty *pro=l->getProeprty();
    if(pro->inherits("QScriptProperty"))
    {
        QScriptEditDialog dlg(pro,this);
        dlg.exec();
    }
    else if(pro->inherits("QStylesheetProperty"))
    {
        QStylesheetEditor dlg(pro,this);
        dlg.exec();
    }
    else if(pro->inherits("QStringProperty"))
    {
        QStringEditDialog dlg(pro,this);
        dlg.exec();
    }
}

void QPropertyListView::closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint)
{
    QTreeWidget::closeEditor(editor,hint);
}

void QPropertyListView::property_value_changed(const QVariant &old, const QVariant &now)
{
    SHAREDLIB::QAbstractProperty *temp=(SHAREDLIB::QAbstractProperty*)sender();
    if(old!=now)
    {
        emit need_make_page_position();
        tagPagePositionInfo l=m_page_position.value<tagPagePositionInfo>();
        l.m_data.insert("QPropertyListView::property",int(temp));
        QVariant v;
        v.setValue<tagPagePositionInfo>(l);
        QMap<QString,QVariant> param;
        param.insert("property",int(temp));
        param.insert("old_value",old);
        param.insert("new_value",now);
        param.insert("page_position",v);
        QPropertyUndoCommand *cmd=new QPropertyUndoCommand(param);
        QUndoStack *stack=CORELIB::QSoftCore::getCore()->get_undo_stack();
        connect(cmd->object(),SIGNAL(redo()),this,SLOT(cammand_property_redo()));
        connect(cmd->object(),SIGNAL(undo()),this,SLOT(cammand_property_undo()));
        stack->push(cmd);
    }
}


void QPropertyListView::cammand_property_redo()
{
    SHAREDLIB::QAbstractProperty* property=(SHAREDLIB::QAbstractProperty*)sender()->property("property").toInt();
    QVariant value=sender()->property("new_value");
    property->setValue(value);
}

void QPropertyListView::cammand_property_undo()
{
    SHAREDLIB::QAbstractProperty* property=(SHAREDLIB::QAbstractProperty*)sender()->property("property").toInt();
    QVariant value=sender()->property("old_value");
    property->setValue(value);
}

bool QPropertyListView::pagePosition(const QVariant &param)
{
    tagPagePositionInfo info=param.value<tagPagePositionInfo>();

    int p=info.m_data.value("QPropertyListView::property").toInt();
    QTreeWidgetItem *item=m_propertyToItem.value((SHAREDLIB::QAbstractProperty*)p);
    this->setCurrentItem(NULL);
    this->setCurrentItem(item);
    return false;
}

void QPropertyListView::page_position(QVariant &)
{

}

void QPropertyListView::set_page_position(const QVariant &info)
{
    m_page_position=info;
}
